package com.sdk.network;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import android.text.TextUtils;

import org.apache.http.protocol.HTTP;

/**
 * WebServiceUrlBuilder of MoboTap Client.
 * @author chzhong
 *
 */
public class WebServiceUrlBuilder {

    private String mServerUrl;
    private String mPath = "";

    // We use this because we have to keep the order of the query keys.
    private final Map<String, String> mQueries = new LinkedHashMap<String, String>();

    /**
     * Initiate a new instance of {@link WebServiceUrlBuilder}.
     * @param serverUrl the host of the web service.
     */
    protected WebServiceUrlBuilder(final String serverUrl) {
        setServerUrl(serverUrl);
    }

    /**
     * Retrieve the serverUrl.
     * @return the value of serverUrl
     */
    protected final String getServerUrl() {
        return this.mServerUrl;
    }

    /**
     * Set the value of host.
     * @param host the host to set
     */
    public final void setServerUrl(final String serverUrl) {
        if (TextUtils.isEmpty(serverUrl)) {
            throw new IllegalArgumentException("server url may not be null.");
        }
        if (serverUrl.endsWith("/")) {
            serverUrl.substring(0, serverUrl.length() -1);
        }
        this.mServerUrl = serverUrl;
    }

    /**
     * Append the service path.
     * @param segements the segments of the path.
     */
    public final void appendPath(final Object...segements) {
        final StringBuilder pathBuilder = new StringBuilder();
        for (final Object segment : segements) {
            pathBuilder.append('/');
            pathBuilder.append(segment);
        }
        this.mPath += pathBuilder.toString();
    }


    /**
     * Set the value of path.
     * @param segements the segments of the path.
     */
    public void setPath(final Object...segements) {
        final StringBuilder pathBuilder = new StringBuilder();
        for (final Object segment : segements) {
            pathBuilder.append('/');
            pathBuilder.append(segment);
        }
        this.mPath = pathBuilder.toString();
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, String value) {
        if (TextUtils.isEmpty(value)) {
            value = "";
        }
        try {
            mQueries.put(name, URLEncoder.encode(value, HTTP.UTF_8));
        } catch (final UnsupportedEncodingException e) {
            // We will never get here because UTF-8 is supported.
        }
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final Object value) {
        if (value != null) {
            addQuery(name, String.valueOf(value));
        } else {
            addQuery(name, "");
        }
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final boolean value) {
        addQuery(name, String.valueOf(value));
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final byte value) {
        addQuery(name, String.valueOf(value));
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final char value) {
        addQuery(name, String.valueOf(value));
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final short value) {
        addQuery(name, String.valueOf(value));
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final int value) {
        addQuery(name, String.valueOf(value));
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final long value) {
        addQuery(name, String.valueOf(value));
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final float value) {
        addQuery(name, String.valueOf(value));
    }

    /**
     * Add a query string.
     * @param name the name of the query.
     * @param value the value of the query.
     */
    public final void addQuery(final String name, final double value) {
        addQuery(name, String.valueOf(value));
    }


    /**
     * Returns the URL.
     * @return the URL.
     */
    public String build() {
        final StringBuilder sb = new StringBuilder();
        sb.append(mServerUrl);
        sb.append(buildPathAndQuery());
        return sb.toString();
    }

    /**
     * Generate the path and query of the service.
     * @return the service.
     */
    public String buildPathAndQuery(){
        final StringBuilder builder = new StringBuilder();
        builder.append(mPath);
        final Set<Entry<String, String>> queries =  mQueries.entrySet();
        if (!queries.isEmpty()) {
            builder.append('?');
            for (final HashMap.Entry<String, String> query : queries) {
                builder.append(query.getKey());
                builder.append('=');
                builder.append(query.getValue());
                builder.append('&');
            }
            // Remove to tailing &
            builder.deleteCharAt(builder.length() -1);
        }
        return builder.toString();
    }

}